home *** CD-ROM | disk | FTP | other *** search
- /******************* ( Animation Construction Kit 3D ) ***********************/
- /* Build Current Viewport */
- /* CopyRight (c) 1993 Author: Lary Myers */
- /*****************************************************************************/
-
- #include <stdlib.h>
- #include <stdio.h>
- #include <dos.h>
- #include <mem.h>
- #include <alloc.h>
- #include <io.h>
- #include <fcntl.h>
- #include <time.h>
- #include <string.h>
- #include <sys\stat.h>
- #include "ack3d.h"
- #include "ackeng.h"
- #include "ackext.h"
-
- void draw_col2(int Col,int slice,int dist,int width,int ht,UCHAR far *Wall,UCHAR far *Screen,UCHAR far *PalTable,int LightFlag,int hoff);
- void AckCopyBackground(UCHAR far *scrn,UCHAR far *bkgd,int len,int offset);
- void DrawSlices(ACKENG *ae);
-
- void FindObject(int xPlayer,int yPlayer,int PlayerAngle,ACKENG *ae);
- UINT xRay(int x,int y,int angle,ACKENG *ae);
- UINT yRay(int x,int y,int angle,ACKENG *ae);
- int FindDoor(int MapPosn,ACKENG *ae);
- long long_sqrt(long v);
-
- void CheckDoors(ACKENG *ae);
-
- extern UCHAR xLight;
- extern UCHAR yLight;
-
- /****************************************************************************
- ** This is the main routine the application should call to create the 3D **
- ** view of the current POV. This routine will build all the walls and **
- ** objects in the current view into the ScreenBuffer pointer within the **
- ** ACKENG structure. No display to the video is done by this routine. The **
- ** application can determine when and how actual display is to be done. **
- ** **
- ****************************************************************************/
- int AckBuildView(ACKENG *ae)
- {
- int i,j,index,xPlayer,yPlayer,PlayerAngle;
- UINT xBitmap,yBitmap,BitmapNumber;
- int ViewAngle;
- int DoorOpenColumn;
- long xDistance,yDistance;
- long WallDistance;
- int distance,LightAdj;
- UINT BitmapColumn,yBitmapColumn;
- int OldMapPosn,OldMapPosn1;
- long xd,yd;
- UINT offset;
-
- CheckDoors(ae);
- xPlayer = ae->xPlayer;
- yPlayer = ae->yPlayer;
- PlayerAngle = ae->PlayerAngle;
-
- /* Begin looking 30 degrees to the left of our current angle */
- ViewAngle = PlayerAngle - INT_ANGLE_30;
-
- if (ViewAngle < 0)
- ViewAngle += INT_ANGLE_360;
-
- MaxDistance = 0;
- LightAdj = 0;
-
- /* Cast two rays for each column of the video display */
-
- for (i = 0; i < VIEW_WIDTH; i++)
- {
- WallDistance = 3000000; /* Set to a ridiculous distance */
- BitmapColumn = -1; /* Set to no walls found */
-
- /* Don't even cast an X ray if impossible to intersect the X walls */
- if (ViewAngle != INT_ANGLE_90 && ViewAngle != INT_ANGLE_270)
- {
- BitmapNumber = xRay(xPlayer,yPlayer,ViewAngle,ae);
-
- if (BitmapNumber) /* A wall was found */
- {
- /* Use the Y intercept to determine the wall column */
- BitmapColumn = (LastY1 >> FP_SHIFT) & 0x3F;
-
- /* Keep the orientation the same no matter which side we're on */
- if ((int)iLastX < xPlayer)
- BitmapColumn = 63 - BitmapColumn;
-
- /* Did we strike a door? */
- if ((BitmapNumber & 0xFF) == DOOR_XCODE)
- {
- index = FindDoor(xMapPosn,ae);
- if (index >= 0) /* This is a valid door */
- {
- j = ae->Door[index].ColOffset; /* Get its current pos */
- offset = 0;
-
- if (BitmapNumber & DOOR_TYPE_SLIDE)
- {
- DoorOpenColumn = 63;
- if ((int)iLastX > xPlayer) /* Handle orientation */
- j = -j;
- BitmapColumn += j; /* Adjust column to show */
- }
-
- if (BitmapNumber & DOOR_TYPE_SPLIT)
- {
- DoorOpenColumn = 31;
- if (BitmapColumn < 32)
- {
- BitmapColumn += j;
- if (BitmapColumn > 31)
- offset = 1;
- }
- else
- {
- BitmapColumn -= j;
- if (BitmapColumn < 32)
- offset = 1;
- }
-
- }
-
-
- if (offset == 1 || BitmapColumn > 63)
- {
- /* Get the grid coordinates for this door */
- OldMapPosn = ae->Door[index].mPos;
- OldMapPosn1 = ae->Door[index].mPos1;
-
- /* Fake the engine into thinking no door is there */
- ae->xGrid[OldMapPosn] = 0;
- ae->xGrid[OldMapPosn1] = 0;
-
- /* Cast the ray to get walls beyond the door */
- BitmapNumber = xRay(xPlayer,yPlayer,ViewAngle,ae);
-
- /* Put back the door codes if not fully open */
- if (ae->Door[index].ColOffset < DoorOpenColumn)
- {
- ae->xGrid[OldMapPosn] = ae->Door[index].mCode;
- ae->xGrid[OldMapPosn1] = ae->Door[index].mCode1;
- }
-
- /* Calc the new bitmap column of wall behind door */
- BitmapColumn = (LastY1 >> FP_SHIFT) & 0x3F;
- if ((int)iLastX < xPlayer)
- BitmapColumn = 63 - BitmapColumn;
- }
- }
- }
-
- /* Calculate the distance to the wall. Since the X position was */
- /* fixed to move 64 or -64 we can use it to determine the actual */
- /* wall distance. The InvCosTable values were stored with a fixed */
- /* point of 20 decimal places. At this time we'll knock off 14 of */
- /* them so we can later multiply with a fixed point value of 16 */
- xd = iLastX - xPlayer;
- WallDistance = (xd * InvCosTable[ViewAngle]) >> 14;
-
- /* Still looking for the reason this may occur. But this check */
- /* will force the distance to a ridiculous value so no wall is */
- /* seen later on when the X and Y walls are compared. */
- if (WallDistance < 0)
- WallDistance = 120000L;
-
- LightAdj = xLight;
- }
-
- }
-
- /* Don't cast a Y ray if its impossible to intercept any Y walls */
- if (ViewAngle != 0 && ViewAngle != INT_ANGLE_180)
- {
- yBitmap = yRay(xPlayer,yPlayer,ViewAngle,ae);
-
- if (yBitmap) /* A wall was found */
- {
-
- /* Use the X intercept to determine the column of the bitmap */
- yBitmapColumn = (LastX1 >> FP_SHIFT) & 0x3F;
-
- /* Handle orientation from either side of the wall */
- if ((int)iLastY > yPlayer)
- yBitmapColumn = 63 - yBitmapColumn;
-
- /* Did we strike a door? */
- if ((yBitmap & 0xFF) == DOOR_YCODE)
- {
- index = FindDoor(yMapPosn,ae);
- if (index >= 0) /* This is a valid door */
- {
- /* Get the current door column offset */
- j = ae->Door[index].ColOffset;
- offset = 0;
-
- /* Deal with orientation */
-
- if (yBitmap & DOOR_TYPE_SLIDE)
- {
- DoorOpenColumn = 63;
- if ((int)iLastY < yPlayer)
- j = -j;
- yBitmapColumn += j;
- }
-
- if (yBitmap & DOOR_TYPE_SPLIT)
- {
- DoorOpenColumn = 31;
- if (yBitmapColumn < 32)
- {
- yBitmapColumn += j;
- if (yBitmapColumn > 31)
- offset = 1;
- }
- else
- {
- yBitmapColumn -= j;
- if (yBitmapColumn < 32)
- offset = 1;
- }
- }
-
- /* If beyond width of bitmap than cast again */
- if (offset == 1 || yBitmapColumn > 63)
- {
-
- /* Get the yGrid coordinates for this door */
- OldMapPosn = ae->Door[index].mPos;
- OldMapPosn1 = ae->Door[index].mPos1;
-
- /* Fool the engine into thinking no door is there */
- ae->yGrid[OldMapPosn] = 0;
- ae->yGrid[OldMapPosn1] = 0;
-
- /* Cast again for walls beyond the door */
- yBitmap = yRay(xPlayer,yPlayer,ViewAngle,ae);
-
- /* Put door code back if not fully open */
- if (ae->Door[index].ColOffset < DoorOpenColumn)
- {
- ae->yGrid[OldMapPosn] = ae->Door[index].mCode;
- ae->yGrid[OldMapPosn1] = ae->Door[index].mCode1;
- }
-
- /* Get the bitmap column of wall beyond door */
- yBitmapColumn = (LastX1 >> FP_SHIFT) & 0x3F;
- if ((int)iLastY > yPlayer)
- yBitmapColumn = 63 - yBitmapColumn;
-
- }
- }
- }
-
-
- /* Calculate the distance to the wall. Since the Y position was */
- /* fixed to move 64 or -64 we can use it to determine the actual */
- /* wall distance. The InvSinTable values were stored with a fixed */
- /* point of 20 decimal places. At this time we'll knock off 14 of */
- /* them so we can later multiply with a fixed point value of 16 */
- yd = iLastY - yPlayer;
- yDistance = (yd * InvSinTable[ViewAngle]) >> 14;
-
- /* Don't know the reason but change negative value into ridiculous */
- if (yDistance < 0)
- yDistance = 120000L;
-
- /* At this point check the distance to the Y wall against the X */
- /* wall to see which one is closer. The closer one is the one */
- /* we'll draw at this column of the screen. */
- if (yDistance < WallDistance)
- {
- WallDistance = yDistance;
- BitmapNumber = yBitmap;
- BitmapColumn = yBitmapColumn;
- LightAdj = yLight;
- }
-
- }
-
- }
-
- if (BitmapColumn < 64) /* A wall was found (either X or Y) */
- {
-
- /* To avoid a fishbowl affect we need to adjust the distance so */
- /* it appears perpendicular to the center point of the display */
- /* which is relative angle 0 from the players current angle. We */
- /* started at -30 degrees for the first screen column and will */
- /* cycle from -30 down to 0 then back up to +30 degrees. This */
- /* cosine value was pre-calculated and placed in ViewCosTable. */
- WallDistance *= ViewCosTable[i];
-
- /* Now we strip off somemore decimal points and check round-up */
- xd = WallDistance >> 14;
- if (WallDistance - (xd << 14) >= 8096)
- xd++;
-
- /* The last decimal points from the multiplication after the X and */
- /* Y rays is stripped off and checked for round-up. */
- WallDistance = xd >> 6;
- if (xd - (WallDistance << 6) >= 32)
- WallDistance++;
-
- /* Don't really need to, but put it into an integer for fast compare */
- distance = WallDistance;
-
- /* This is an arbitrary minimum distance to look for */
- if (distance < 10)
- distance = 10;
-
- /* Don't want it to go outside our table boundaries */
- if (distance >= MAX_DISTANCE)
- distance = MAX_DISTANCE - 1;
-
- /* Save the wall data to display when done with entire screen */
- Walls[i].Distance = distance;
- Walls[i].Number = BitmapNumber & 0xFF;
- Walls[i].Column = BitmapColumn;
- Walls[i].LightAdj = LightAdj;
-
- if (distance > MaxDistance)
- MaxDistance = distance;
- }
-
- ViewAngle++;
- if (ViewAngle >= INT_ANGLE_360)
- ViewAngle -= INT_ANGLE_360;
-
- }
-
- DrawSlices(ae);
-
- /* Now we look at any objects that may be closer than the walls */
- FindObject(xPlayer,yPlayer,PlayerAngle,ae);
-
- return(0);
- }
-
- /****************************************************************************
- ** Internal routine used to draw the walls, called by AckDrawView(). **
- ** **
- ****************************************************************************/
- void DrawSlices(ACKENG *ae)
- {
- int i,x1,wt,ht,LightFlag,hoff;
- UCHAR far *wall,*ScreenBuffer;
- UCHAR far *pTable;
- UCHAR far **bmaps;
-
- AckCopyBackground(ae->ScreenBuffer,ae->BkgdBuffer,ae->WinLength,ae->WinStartOffset);
-
- x1 = ae->WinEndX;
-
- ht = ae->CenterRow;
- wt = BYTES_PER_ROW;
- hoff = ht * wt;
- ScreenBuffer = ae->ScreenBuffer;
- bmaps = ae->bMaps;
- pTable = ae->PalTable;
- LightFlag = ae->LightFlag;
-
- for (i = ae->WinStartX; i < x1; i++)
- {
- wall = AckGetBitmapPtr(Walls[i].Number,bmaps);
- draw_col2(i,Walls[i].Column,Walls[i].Distance,wt,ht,wall,
- ScreenBuffer,pTable,LightFlag,hoff);
- }
- }
-
-
- /****************************************************************************
- ** Internal routine used to determine which objects are visible in the **
- ** current POV. There is still one major anomaly with this routine in that **
- ** "ghost" objects tend to appear at certain angles and distances. Any **
- ** thoughts/comments on why this occurs would be greatly appreciated! **
- ** **
- ****************************************************************************/
- void FindObject(int xPlayer,int yPlayer,int PlayerAngle,ACKENG *ae)
- {
- int i,j,count,SaveCenter;
- int ObjX,ObjY,ObjNum;
- int NewX,NewY,LightFlag;
- int MaxOpp,Column,ColBeg,ColEnd;
- int wt,ObjIndex,CenterColumn;
- int vidwt,vidht,hoff;
- long deltax,deltay;
- long xp,yp,distance;
- long SinValue,CosValue;
- UCHAR far *wall,*ScreenBuffer;
- UCHAR far *pTable;
- UCHAR far **omaps;
-
- char mBuf[40];
-
- vidht = ae->CenterRow;
- vidwt = BYTES_PER_ROW;
- hoff = vidht * vidwt;
- ScreenBuffer = ae->ScreenBuffer;
- omaps = ae->oMaps;
-
-
- TotalObjects = 0;
- ObjRelDist[0] = 0L;
- SinValue = SinTable[PlayerAngle];
- CosValue = CosTable[PlayerAngle];
-
- /* First thing we'll do is check all the objects to see which ones may be */
- /* completely out of view, and to get some initial values for later... */
- for (i = 0; i < MAX_OBJECTS; i++)
- {
- if (!ae->ObjList[i].Active)
- continue;
-
- ObjX = ae->ObjList[i].x;
- ObjY = ae->ObjList[i].y;
-
- /* Translate the object coordinates to make relative to the POV */
- NewX = ObjX - xPlayer;
- NewY = ObjY - yPlayer;
-
- if (abs(NewX) < 10 && abs(NewY) < 10)
- continue;
-
-
- #if 0
- if (PlayerAngle > INT_ANGLE_180 && (NewY-63) > 0)
- continue;
-
- if (PlayerAngle < INT_ANGLE_180 && (NewY+63) < 0)
- continue;
-
- if (PlayerAngle > INT_ANGLE_270 || PlayerAngle < INT_ANGLE_90)
- {
- if ((NewX+63) < 0)
- continue;
- }
-
- if (PlayerAngle < INT_ANGLE_270 && PlayerAngle > INT_ANGLE_90)
- {
- if ((NewX-63) > 0)
- continue;
- }
- #endif
-
- if ((PlayerAngle == 0 || PlayerAngle == INT_ANGLE_180) && abs(NewX) < 2)
- continue;
-
- if ((PlayerAngle == INT_ANGLE_90 || PlayerAngle == INT_ANGLE_270) &&
- abs(NewY) < 2)
- continue;
-
-
-
- /* Rotate coordinates to current player angle */
- xp = ((NewX * CosValue) + (NewY * SinValue)) >> FP_SHIFT;
- yp = ((NewY * CosValue) - (NewX * SinValue)) >> FP_SHIFT;
-
- if (xp <= 0)
- continue;
-
- distance = (long)long_sqrt((xp * xp) + (yp * yp));
-
- if (distance >= MAX_DISTANCE)
- continue;
-
- /* Place the objects in the correct order so further ones are behind */
- j = TotalObjects;
- if (j)
- {
- for (count = 0; count < TotalObjects; count++)
- {
- if (distance > ObjRelDist[count])
- {
- for (j = TotalObjects; j > count; j--)
- {
- ObjRelDist[j] = ObjRelDist[j-1];
- ObjNumber[j] = ObjNumber[j-1];
- ObjDeltaX[j] = ObjDeltaX[j-1];
- ObjDeltaY[j] = ObjDeltaY[j-1];
- }
-
- j = count;
- count = TotalObjects;
- }
- }
- }
- /* Hold onto relavant data for the object found */
- ObjNumber[j] = i;
- ObjRelDist[j] = distance;
- ObjDeltaX[j] = xp;
- ObjDeltaY[j] = yp;
- TotalObjects++;
- ObjRelDist[TotalObjects] = 0L;
- }
-
- /* Didn't find any objects on the above pass, so we're done */
- if (!TotalObjects)
- return;
-
- CenterColumn = ae->WinStartX + (ae->WinWidth / 2);
-
- pTable = ae->PalTable;
- LightFlag = ae->LightFlag;
-
- for (i = 0; i < TotalObjects; i++)
- {
- ObjIndex = ObjNumber[i];
- ObjNum = ae->ObjList[ObjIndex].bmNum[ae->ObjList[ObjIndex].CurNum];
-
- ObjY = ae->ObjList[ObjIndex].y;
- NewY = ObjY - yPlayer;
-
- distance = ObjRelDist[i];
- deltax = ObjDeltaX[i];
- yp = deltay = ObjDeltaY[i];
-
- MaxOpp = ((LongTanTable[INT_ANGLE_30] * (long)deltax) >> FP_SHIFT);
-
- if (NewY < ObjY)
- {
- MaxOpp = -MaxOpp;
- yp = -yp;
- }
-
- if ((yp+32) < MaxOpp)
- continue;
-
- Column = CenterColumn;
-
- if (MaxOpp)
- Column = CenterColumn - ((deltay * CenterColumn) / MaxOpp);
-
- yp = ViewCosTable[Column];
- if (yp)
- {
- distance = distance * yp;
-
- xp = distance >> 14;
- if (distance - (xp << 14) >= 8096)
- xp++;
-
- distance = xp;
- }
-
- if (distance < 0)
- continue;
-
- if (distance >= (MAX_DISTANCE - 10))
- distance = MAX_DISTANCE-11;
-
- wt = DistanceTable[distance];
-
- /* Keep the width of the object reasonable */
-
- if (wt > 300)
- continue;
-
- if (wt < 16) wt = 16;
-
- yp = AdjustTable[distance];
- xp = 0; /* First col of the object to display */
-
- NewX = Column;
-
- if (ae->ObjList[ObjIndex].Sides)
- ObjNum =
- ae->ObjList[ObjIndex].bmNum[((PlayerAngle / ae->ObjList[ObjIndex].Sides) & 7)];
-
- SaveCenter = ae->CenterRow;
- ae->CenterRow = ae->ObjList[ObjIndex].VidRow;
- ColEnd = NewX + wt;
-
- for (Column = NewX - wt; Column < ColEnd; Column++)
- {
- if (Column >= ae->WinStartX && Column <= ae->WinEndX)
- {
- if (distance < (Walls[Column].Distance + 10))
- {
- wall = AckGetBitmapPtr(ObjNum,omaps);
-
- draw_col2(Column,xp >> FP_SHIFT,distance,vidwt,vidht,wall,
- ScreenBuffer,pTable,LightFlag,hoff);
- }
-
- }
- xp += yp; /* Advance the next column to display (scaling) */
- }
-
- ae->CenterRow = SaveCenter;
- }
-
- }
-
-